package com.xiaobai.util;

import com.xiaobai.common.base.CommonRuntimeException;
import com.xiaobai.common.base.ErrorCode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

@Slf4j
public class FileUtil {

    /**
     * 创建一个file文件
     * 若文件夹不存在, 则创建相应的文件夹
     * 若文件已经存在, 则先删除
     *
     * @param filePath 文件全路径
     */
    public static File newFile(String filePath) {
        File file = new File(filePath);
        if (!file.getParentFile().exists()) {
            file.getParentFile().mkdirs();
        }
        if (file.exists()) {
            file.delete();
        }
        try {
            file.createNewFile();
        } catch (IOException e) {
            throw new CommonRuntimeException(ErrorCode.COMMON_FAIL, e, String.format("createNewFile出错,path==>%s", filePath));
        }
        return file;
    }

    /**
     * 将多行文本 追加到file文件尾部
     *
     * @param file     文件
     * @param strLines 待追加的多行文本
     */
    public static void appendLines(File file, Collection<String> strLines) {
        writeLines(file, strLines, true);
    }

    /**
     * 将多行文本 覆盖写入file文件
     *
     * @param file     文件
     * @param strLines 待写入的文本List
     */
    public static void writeLines(File file, Collection<String> strLines) {
        writeLines(file, strLines, false);
    }

    /**
     * 将多行文本写入file中(追加 or 覆盖)
     *
     * @param file       文件
     * @param strLines   待写入的文本List
     * @param appendFlag 是否追加到尾部
     */
    private static void writeLines(File file, Collection<String> strLines, boolean appendFlag) {
        try (FileOutputStream out = new FileOutputStream(file, appendFlag);
             OutputStreamWriter outputStreamWriter = new OutputStreamWriter(out, StandardCharsets.UTF_8)
        ) {
            // lineEnding传null, 会根据系统环境决定使用\r\n 还是 \n
            IOUtils.writeLines(strLines, null, outputStreamWriter);
        } catch (IOException e) {
            throw new CommonRuntimeException(ErrorCode.COMMON_FAIL, e, "writeList写文件出错");
        }
    }

    /**
     * 在线解压ZIP文件至指定目录
     *
     * @param urlStr    远程服务器下载地址
     * @param unpackDir 解压目录
     */
    public static void unZipFromUrl(String urlStr, String unpackDir) {
        HttpURLConnection httpConnection = null;
        ZipInputStream zipInputStream = null;
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        try {
            httpConnection = (HttpURLConnection) new URL(urlStr).openConnection();
            httpConnection.setConnectTimeout(5 * 1000);// 设置建立连接的超时间
            httpConnection.setReadTimeout(30 * 1000);// 设置传递数据的时间

            // 1.获取zip流
            zipInputStream = new ZipInputStream(httpConnection.getInputStream(), StandardCharsets.UTF_8);
            bis = new BufferedInputStream(zipInputStream);

            ZipEntry entry;
            byte[] b;
            int len;
            // 2.依次处理zip中的所有文件
            while ((entry = zipInputStream.getNextEntry()) != null) {
                File outFile = new File(unpackDir + entry.getName());
                if (entry.isDirectory()) {
                    continue;
                }
                if (outFile.getParentFile() != null && !outFile.getParentFile().exists()) {
                    outFile.getParentFile().mkdirs();
                }
                if (!outFile.exists()) {
                    outFile.createNewFile();
                }

                // 3.将zip中每个文件写入磁盘
                bos = new BufferedOutputStream(new FileOutputStream(outFile));
                b = new byte[1024];
                while ((len = bis.read(b)) != -1) {
                    bos.write(b, 0, len);
                }
                bos.flush();
            }
        } catch (IOException e) {
            throw new CommonRuntimeException(ErrorCode.COMMON_FAIL, e, "获取网络zip文件出错");
        } finally {
            try {
                if (bos != null) {
                    bos.close();
                }
                if (bis != null) {
                    bis.close();
                }
                if (zipInputStream != null) {
                    zipInputStream.close();
                }
                if (httpConnection != null) {
                    httpConnection.disconnect();
                }
            } catch (IOException e) {
                log.error("关闭流 error", e);
            }
        }
    }

    /**
     * 删除文件
     *
     * @param filePath 文件路径
     */
    public static void delFile(String filePath) {
        delFile(new File(filePath));
    }

    /**
     * 删除文件
     *
     * @param file
     */
    public static void delFile(File file) {
        if (file != null) {
            if (file.isFile() && file.exists()) {
                file.delete();
            }
        }
    }

    /**
     * 清空文件夹下所有文件
     *
     * @param dirPath 路径
     */
    public static void delDir(String dirPath) {
        delDir(new File(dirPath));
    }

    /**
     * 清空文件夹下所有文件
     *
     * @param file 文件
     */
    public static void delDir(File file) {
        if (file != null) {
            if (file.exists()) {
                if (file.isDirectory()) {
                    File[] zFiles = file.listFiles();
                    if (zFiles != null) {
                        for (File file2 : zFiles) {
                            delDir(file2);
                        }
                    }
                    file.delete();
                } else {
                    file.delete();
                }
            }
        }
    }

    /**
     * 转换文件大小为可读的B/KB/MB/GB
     *
     * @param fileSizeLimit 文件大小(单位:B)
     * @return 文件大小描述, 如: "20KB"
     */
    public static String getFileSizeStr(long fileSizeLimit) {
        if (fileSizeLimit < 1024) {
            return fileSizeLimit + "B";
        } else if (fileSizeLimit < 1024 * 1024) {
            return fileSizeLimit / 1024 + "KB";
        } else if (fileSizeLimit < 1024 * 1024 * 1024) {
            return fileSizeLimit / 1024 / 1024 + "MB";
        } else {
            return fileSizeLimit / 1024 / 1024 / 1024 + "GB";
        }
    }
}
